
#ifndef LEVELMANAGER_H
#define LEVELMANAGER_H

#include <vector>
#include "Singleton.h"
#include "Texture.h"
#include "Baby.h"
#include "Food.h"
#include "RenderableEntity.h"
#include "AnimatedEntity.h"
#include "Prize.h"
#include "defs.h"
#include "TextEntity.h"
#include "TimeLineEffect.h"

struct BabyData
{
    int iIndex;
    std::string sName;
    std::string sCaption;
    bool bAvailable;
    Vector2 vMouthOffset;
    int iScore;
    Vector4 vColor;
    
    std::string sFood;
    bool bFoodAnimated;
    
    std::string sPrize;
    
    int iPrice;
    int iLevelUpPrice[MAX_BABY_LEVEL];
};

struct PrizeData
{
    float fSpawnProbability;
    int iScore;
};

typedef std::vector<Baby*> BabiesVector;
typedef std::vector<Food*> FoodVector;
typedef std::vector<Prize*> PrizeVector;
typedef std::vector<std::string> StringVector;
typedef std::vector<std::string> AvailableBabiesVector;
typedef std::map<std::string, BabyData> BabiesDataMap;
typedef std::map<std::string, PrizeData> PrizesDataMap;

const float MAX_BAR_TIME = 10.0f;
const float INITIAL_TIME = 10.0f;

const float CHAIN_MULTIPLIER = 0.1f;

class LevelManager : public Singleton<LevelManager>
{
public:
	enum tGameState { Countdown, Normal, EndGame, GameOver };
    enum tRoundType { OneBaby, ThreeBabies, BigBaby };
	
private:
    int     m_iHearts;
    int     m_iScoreInARow;
    int     m_iScore;
    int     m_iMaxCombo;
    int     m_iPrizeCount;
    
	tGameState gameState;
    float m_fStateTimer;
    
    // tutorials
    float m_fLastAlpha;
    RenderableEntity* m_oTutorialArrow;
    
    // power ups
    bool m_bClickOutOfPowerUps;
    bool m_bRainbowUsed;
    RenderableEntity* m_oExtraRainbowButton;
    TextEntity* m_oExtraRainbowNumber;
    bool m_bBulletTimeUsed;
    float m_fBulletTimeLeft;
    float m_fNoRainbowTimer;
    RenderableEntity* m_oBulletTime;
    TextEntity* m_oBulletTimeNumber;
    bool m_bDoubleCoinsUsed;
    RenderableEntity* m_oDoubleCoins;
    TextEntity* m_oDoubleCoinsNumber;
    bool m_bChainBoostUsed;
    RenderableEntity* m_oChainBoost;
    TextEntity* m_oChainBoostNumber;
    bool m_bSuperChainBoostUsed;
    RenderableEntity* m_oSuperChainBoost;
    TextEntity* m_oSuperChainBoostNumber;
    bool m_bPrizeSeasonUsed;
    RenderableEntity* m_oPrizeSeason;
    TextEntity* m_oPrizeSeasonNumber;
    
    BabiesVector babies; 
    FoodVector foods;
    PrizeVector prizes;
    
    int m_iTotalBabies;
    
    int m_aiCurrentBabyIndexes[3];
    int m_iNextBabyIndex;
    
    float m_fTime;
    
    // available babies
    AvailableBabiesVector m_vecsAvailableBabies;
    
    // all babies data
    StringVector m_vsAllBabiesNamesInOrder; // all babies of the game are here in the same order as the xml and the babies shop screen
    BabyData m_asBabyData[MAX_BABY_TYPES];
    BabiesDataMap m_moBabiesData;
    PrizesDataMap m_moPrizesData;
    int m_iScoreToUnlockNewBaby;
    int m_iAvailableBabies;
    
    // gameplay stuff to configure the game
    bool m_bDiscardEnabled;
    float m_fDiscardProbability;
    bool m_bPrizeTookThisFrame;
    
    // game speed multiplier
    float m_fGameSpeed;
    
    // renderable entities
    TextEntity* m_oTextReady;
    TextEntity* m_oTextFeed;
    
    TimeLineEffect* m_oReadyShow;
    TimeLineEffect* m_oFeedShow;
    
    // food trail, always use the same to allocate memory once
    ParticleSystem* m_oFoodTrail;
    ParticleSystem* m_oFedSuccesParticles;

    // clouds that will be using babies
    AnimatedEntity* m_aoClouds[4];
    RenderableEntity* m_aoShadows[4];
    int m_iFreeCloud;
    int m_iRounds;
    
    int m_iCorrectDiscarded;
    // special game states
    bool m_bThreeBabiesShown;
    bool m_bBigBabyShown;
    int m_iSpecialRound;
    bool m_bStartNormalAfterSpecial;
    float m_fAfterSpecialTimer;
    float m_fSpecialTimer;
    int m_iEndSpecialRound;
    
    static const int FINGER_TRAIL_COUNT = 500;
    struct fingerTrailItem
    {
        RenderableEntity* m_oIcon;
        float m_fTime;
    };
    fingerTrailItem m_asFingerTrail[FINGER_TRAIL_COUNT];
    int m_iFingerTrailIndex;
    Vector2 m_vLastPressedPos;
    
public:
	LevelManager():m_bDiscardEnabled(true), m_fDiscardProbability(0.1f), m_fGameSpeed(1.0f){}
	~LevelManager();
	
public:
    void updateTutorials();
    bool isThreeBabiesRound() { return m_iSpecialRound == ThreeBabies; }
    void setEndSpecialRound() { m_iEndSpecialRound++; }
    bool getPrizeTookThisFrame() { return m_bPrizeTookThisFrame; }
    float getGameSpeed() { return m_fGameSpeed; }
    Vector2 getMouthPos(std::string _sBaby) { return m_moBabiesData[_sBaby].vMouthOffset; }
	int getScore() { return m_iScore; }
	int getCoins() { return m_iHearts; }
    float getChainMultiplier();
    ParticleSystem* getFoodTrail() { return m_oFoodTrail; }
    AnimatedEntity* getBabyCloud(int i) { return m_aoClouds[i]; }
    RenderableEntity* getBabyShadow(int i) { return m_aoShadows[i]; }
    int getFreeCloud() { return m_iFreeCloud; }
	
	tGameState getGameState() { return gameState; }
	
    void addRound() { m_iRounds++; }
    
	void init();
	void update(float dt);
	void render();
    void renderPowerUps();
    
    void startCountdown();
	void startGame(bool _bExtraRainbow);
    void reset();
    void endGame();
    void updateEndGame();
    void updateCountdown();
    
    int getRandomAvailableAnimalIndex();
    void readBabiesAndFoodsXML();
    void fedCorrectBaby(Baby* _oBaby);
    void fedWrongBaby(Baby* _oBaby);
    void discardedFood(std::string _sFood);
    void foodGone(std::string _eFood);
    
    //Food
	void updateFood();
    void spawnFood();
    
    //Babies
	void updateBabies();
    void updateAvailableBabies();
    void spawnBaby(eBabyPos _iBabyPos, int _iCloud);
    void setNextBabyType(eBabyPos _iBabyPos);
    
    int getNextActiveBabyNotAvailable();
    void initAvailableBabies();
    
    void addTime(float _fTimeToAdd);
    
    // prizes
    void updatePrizes();

    // helper functions
    bool isBabyInGame(std::string _sBaby);
    bool babyAndFoodMatch(std::string _sBaby, std::string _sFood);
    std::string getFoodFromBaby(std::string _sBaby);
    std::string getAvailableBabyNotInGame();
    int getIndexOfBabyInRoster(std::string _sBaby);
    void makeFoodAndBabiesDisappear();
    
	Baby* getBabyInPos(eBabyPos _iBabyPos);
    
    //Scores
    void correctBaby();
    void wrongBaby();
    
    void addHearts(int _iHearts, Vector2 _vPosition);
    
    BabyData getBabyData(std::string _sName);
    BabyData getBabyData(int _iIndex);
    PrizeData getPrizeData(std::string _sName);
    
    // power ups
    void updateStartPowerUps();
    void useBulletTime();
    void useExtraRainbow();
    
    // special methods
    void checkSpecialStates();
    void startSpecialRound(int _iRoundType);
    void startNormalRoundAfterSpecial();
    void prepareNormalRoundAfterSpecial();
    
    std::string getBabyName(int _iIndex);
    int getBabyIndex(std::string _sName);
    
    void gameOver();
};

#endif
